import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.Random;

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Game2048 extends JPanel {
    private static final int SIZE = 4;
    private static final int SQUARE_A = 100;
    private int[][] grid = new int[SIZE][SIZE];
    private final Random random = new Random();
    private boolean win=false;

    private boolean lose=false;

    Game2048() {
        //生成两个随机值，填入grid
        setFocusable(true);



        addKeyListener(new KeyAdapter(){

            

            public void KeyPressed (KeyEvent e) {
                super.keyTyped(e);
                if (!win&&!lose)
                {
                    switch (e.getKeyCode())
                    {
                        case KeyEvent.VK_LEFT -> moveLeft();
                        case KeyEvent.VK_RIGHT -> moveRight();
                        case KeyEvent.VK_UP ->moveUp();
                        case KeyEvent.VK_DOWN -> moveDown();
                    }
                }

                repaint();
                spawnTile();
                checkGameState();
            }
        });
        spawnTile();
        spawnTile();

        };



    private void moveLeft(){
        for (int row=0;row<SIZE;row++)
        {
            int[] mergedRow=new int[SIZE];
            Arrays.fill(mergedRow,0);
            boolean[]alreadyMerged=new boolean[SIZE];
            Arrays.fill(alreadyMerged,false);
            int colIndex=0;//答案数组中的第几个
            for (int col=0;col<SIZE;col++)
            {
                if (grid[row][col]!=0){
                    if (colIndex==0){
                        mergedRow[colIndex]=grid[row][col];
                        colIndex++;

                    }else {
                        if (grid[row][col]==mergedRow[colIndex-1]&&alreadyMerged[colIndex-1])
                        {
                            mergedRow[colIndex-1]*=2;
                            alreadyMerged[colIndex]=true;

                        }
                        else {
                            mergedRow[colIndex]=grid[row][col];
                            colIndex++;
                        }

                    }


                }

            }

            System.arraycopy(mergedRow,0,grid[row],0,SIZE);
        }




    }

    private void moveRight()
    {
        //180
        rorateRight();
        rorateRight();
        moveLeft();
        rorateRight();
        rorateRight();
    }

    private void moveUp()
    {
        //270
        rorateRight();
        rorateRight();
        rorateRight();
        moveLeft();
        //90
        rorateRight();
    }
    private void moveDown()
    {
        //90
        rorateRight();
        moveLeft();
        //270
        rorateRight();
        rorateRight();
        rorateRight();
    }

    private void rorateRight()
    {
        int[][]newGrid=new int [SIZE][SIZE];
        for (int row=0;row<SIZE;row++)
        {
            for (int col=0;col<SIZE;col++)
            {newGrid[col][SIZE-1-row]=grid[row][col];

            }
        }
        grid=newGrid;
    }

    private void  checkGameState(){
        int cnt=0;
        for(int row=0;row<SIZE;row++)
        {
            for(int col=0;col<SIZE;col++){

                if(grid[row][col]==2048){
                    win=true;
                    break;
                }else {
                    if(grid[row][col]!=0)
                    {
                        cnt++;

                    }


                }

            }
            if (win)
                break;

        }
        if(!win){
            if(cnt<SIZE*SIZE)
            {
                return;

            }
            for (int row=0;row<SIZE;row++)
            {
                for(int col=0;col<SIZE;col++)
                {
                    if (row<SIZE-1 && grid[row][col]==grid[row+1][col])
                    {
                        return;

                    }
                    if (col<SIZE-1 && grid[row][col]==grid[row][col+1])
                    {
                        return;

                    }


                }


            }



        }
    }
    private void spawnTile() {
        int cnt=0;
        for (int row=0;row<SIZE;row++) {
            for (int col = 0; col < SIZE; col++)
            {
                if(grid[row][col]!=0)
                {
                    cnt++;
                }
            }

        }
        if (cnt==SIZE*SIZE)
        {
            return;
        }
        int x, y;
        do {
            x = random.nextInt(SIZE);
            y = random.nextInt(SIZE);
        } while (grid[x][y] != 0);
        grid[x][y] = random.nextInt(10) < 5 ? 2 : 4;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(0, 0, getWidth(), getHeight());
        for (int row = 0; row < 4; row++) {
            for (int col = 0; col < 4; col++) {
                //画小正方形
                drawTile(g, grid[row][col], col * SQUARE_A, row * SQUARE_A);
            }
        }
        g.setColor(Color.BLACK);
        if(win)
        {

            g.drawString("You Win!",getWidth()/2,getHeight()/2);

        }
        if(lose)
        {

            g.drawString("Game Over!",getWidth()/2,getHeight()/2);

        }
    }

    private void drawTile(Graphics g, int value, int x, int y) {
        //RGB (0,255)
        int m = (int) (Math.log(value) / Math.log(2));
        g.setColor(value == 0 ? Color.GRAY : new Color(255 - m * 20, 240, 200));
        g.fillRect(x, y, SQUARE_A, SQUARE_A);
        g.setColor(Color.BLACK);
        g.drawRect(x, y, SQUARE_A, SQUARE_A);

        if(value != 0) {
            FontMetrics fm = g.getFontMetrics();
            int textX = x + (SQUARE_A - fm.stringWidth(String.valueOf(value)))/2;
            int textY = y + (SQUARE_A - fm.getHeight())/2 + fm.getAscent();
            g.drawString(String.valueOf(value), textX, textY);
        }

    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("2048 Game");
        Game2048 game2048 = new Game2048();
        frame.setSize(420, 460);
        frame.add(game2048);
        frame.setVisible(true);
    }
}